﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;

namespace O1.Data
{
    [ContractVerification(true)]
    [StructLayout(LayoutKind.Explicit)]
    public struct CellAddress : IEquatable<CellAddress>
    {
        public const int Size = Sz.Cell.CellAddress;

        [FieldOffset(0)]
        [CLSCompliant(false)]
        public readonly uint CellId;

        [FieldOffset(Sz.CLR.Int32)]
        [CLSCompliant(false)]
        public readonly ushort TrunkId;

        [CLSCompliant(false)]
        public CellAddress(ushort trunkId, uint cellId)
            : this()
        {
            this.CellId = cellId;
            this.TrunkId = trunkId;
        }

        public static bool operator ==(CellAddress instance, CellAddress other)
        {
            return instance.Equals(other);
        }

        public static bool operator !=(CellAddress instance, CellAddress other)
        {
            return !instance.Equals(other);
        }

        public bool Equals(CellAddress other)
        {
            return
                this.CellId == other.CellId &&
                this.TrunkId == other.TrunkId;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (obj.GetType() != typeof(CellAddress))
            {
                return false;
            }

            return this.Equals((CellAddress)obj);
        }

        public override int GetHashCode()
        {
            return (this.CellId ^ this.TrunkId.GetHashCode()).GetHashCode();
        }

        internal static unsafe CellAddress Read(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);
            
            fixed (byte* p = &buffer[offset])
            {
                return *((CellAddress*)p);
            }
        }

        internal static unsafe CellAddress ReadLocalId(ushort trunkId, byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Sz.CLR.Int32 < buffer.Length, ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                return new CellAddress(trunkId, *((uint*)p));
            }
        }

        internal static unsafe void WriteLocalId(uint localId, byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Sz.CLR.Int32 < buffer.Length, ExceptionText.ValueBufferTooSmall);

            fixed (byte* p = &buffer[offset])
            {
                *((uint*)p) = localId;
            }
        }

        internal unsafe void Write(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);
            
            fixed (byte* p = &buffer[offset])
            {
                *((CellAddress*)p) = this;
            }
        }
    }
}